media: Add URL-player renderer client and GetMediaUrl - #10998
Conversation
🤖 Gemini Suggested Commit Message💡 Pro Tips for a Better Commit Message:
|
Add use_starboard_url_player GN capability flag, derived from use_starboard_media and tvOS platform detection. Expose it as BUILDFLAG(USE_STARBOARD_URL_PLAYER) through build_config.h and register matching Mojo enabled_features entry. Add RendererType::kUrlPlayer = 13 to the C++ enum, Mojom enum, traits mapping, and histogram. The new enum is gated behind USE_STARBOARD_URL_PLAYER so non-tvOS builds are unaffected. This establishes the capability boundary for all subsequent URL-player CLs. Bug: 512045535
Introduce InitializeWithUrl(client, source_url) on the Mojo renderer pipe to carry the media URL from the renderer process to the GPU process, keeping URL delivery and initialization ordered on the same pipe. On the GPU side, add UrlPlayerRenderer for tvOS URL-based playback via SbPlayerBridge in punch-out mode, and UrlPlayerRendererWrapper as the Mojo service bridge. The wrapper sets the URL on the renderer then calls Initialize(). On the renderer-process side, MojoRenderer and MojoRendererWrapper forward InitializeWithUrl over the Mojo pipe. MojoRendererService validates the renderer type and URL before forwarding, with runtime guards for unexpected state and invalid URLs. Bug: 512045535
Implement UrlPlayerRendererClient for the renderer-process side of URL-based playback on tvOS. The client extends MojoRendererWrapper, invokes InitializeWithUrl() on the Mojo renderer pipe, and implements StarboardRendererClientExtension callbacks for video-hole painting, rendering mode updates, and SbWindow handle delivery. Add MediaResource::GetMediaUrl() behind USE_STARBOARD_URL_PLAYER so URL-based MediaResource implementations can provide the source URL. The default returns an empty GURL; UrlPlayerDemuxer overrides it in a later CL. Build: ninja -C out/tvos-arm64-simulator_qa cobalt nplb media_unittests Bug: 512045535
|
cc : @rakuco @jkim-julie @Gyuyoung |
There was a problem hiding this comment.
Code Review
This pull request introduces a new URL-based player renderer (UrlPlayerRenderer) and its associated Mojo wrapper and client components to support platform-mediated playback (such as HLS via AVPlayer on tvOS) using punch-out mode. The review feedback highlights several opportunities to improve robustness and error handling. Key recommendations include adding null checks for client_ and cdm_context to prevent potential null pointer dereferences, gracefully handling a null video_geometry_setter_service_ during initialization, notifying the client of errors when encountering unsupported rendering modes post-initialization, adding a default case for unknown player errors, and correctly handling counter resets for decoded or dropped frames to ensure accurate statistics reporting.
| if (!init_cb_.is_null()) { | ||
| std::move(init_cb_).Run(PIPELINE_ERROR_DISCONNECTED); | ||
| return; | ||
| } | ||
| client_->OnError(PIPELINE_ERROR_DISCONNECTED); |
There was a problem hiding this comment.
If a connection error occurs after initialization has failed or completed, client_ might be null or the object might be in a state where client_ is not set. Adding a null check for client_ prevents potential null pointer dereference crashes.
if (!init_cb_.is_null()) {
std::move(init_cb_).Run(PIPELINE_ERROR_DISCONNECTED);
return;
}
if (client_) {
client_->OnError(PIPELINE_ERROR_DISCONNECTED);
}| if (!init_cb_.is_null()) { | ||
| std::move(init_cb_).Run(pipeline_status()); | ||
| } | ||
| return; | ||
| case StarboardRenderingMode::kInvalid: | ||
| LOG(ERROR) << "Invalid rendering mode."; | ||
| SetMojoRendererInitialized( | ||
| PipelineStatus(PIPELINE_ERROR_INITIALIZATION_FAILED, | ||
| "Invalid rendering mode for URL player")); | ||
| if (!init_cb_.is_null()) { | ||
| std::move(init_cb_).Run(pipeline_status()); | ||
| } |
There was a problem hiding this comment.
If the rendering mode changes to an unsupported mode (like kDecodeToTexture or kInvalid) after initialization has already completed (meaning init_cb_ is null), the client should be notified of the error via client_->OnError(...) rather than silently returning.
if (!init_cb_.is_null()) {
std::move(init_cb_).Run(pipeline_status());
} else if (client_) {
client_->OnError(pipeline_status());
}
return;
case StarboardRenderingMode::kInvalid:
LOG(ERROR) << "Invalid rendering mode.";
SetMojoRendererInitialized(
PipelineStatus(PIPELINE_ERROR_INITIALIZATION_FAILED,
"Invalid rendering mode for URL player"));
if (!init_cb_.is_null()) {
std::move(init_cb_).Run(pipeline_status());
} else if (client_) {
client_->OnError(pipeline_status());
}| // Subscribe to video geometry changes. | ||
| DCHECK(video_geometry_setter_service_); | ||
| video_geometry_setter_service_->GetVideoGeometryChangeSubscriber( |
There was a problem hiding this comment.
video_geometry_setter_service_ is a raw pointer and could potentially be null. Checking it and failing initialization gracefully with an error callback is safer than relying solely on DCHECK which is disabled in debug builds.
| // Subscribe to video geometry changes. | |
| DCHECK(video_geometry_setter_service_); | |
| video_geometry_setter_service_->GetVideoGeometryChangeSubscriber( | |
| if (!video_geometry_setter_service_) { | |
| std::move(init_cb).Run(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| return; | |
| } | |
| // Subscribe to video geometry changes. | |
| video_geometry_setter_service_->GetVideoGeometryChangeSubscriber( |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); | ||
| DCHECK(cdm_context); | ||
|
|
||
| if (cdm_context_ || SbDrmSystemIsValid(drm_system_)) { |
There was a problem hiding this comment.
In release builds, cdm_context could potentially be null. Adding a null check prevents a null pointer dereference when calling cdm_context->GetSbDrmSystem().
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (!cdm_context) {
std::move(cdm_attached_cb).Run(false);
return;
}
if (cdm_context_ || SbDrmSystemIsValid(drm_system_)) {| case kSbPlayerErrorMax: | ||
| NOTREACHED(); | ||
| break; | ||
| } |
There was a problem hiding this comment.
If a new error is added to SbPlayerError in the future, the switch statement will not handle it, causing the player to transition to STATE_ERROR without notifying the pipeline. Adding a default case ensures that any unknown errors are handled and reported gracefully.
case kSbPlayerErrorMax:
NOTREACHED();
break;
default:
MEDIA_LOG(ERROR, media_log_) << "Unknown player error: " << error << " message: " << message;
NotifyError(PIPELINE_ERROR_DECODE);
break;
}| void UrlPlayerRenderer::NotifyError(PipelineStatus status) { | ||
| if (init_cb_) { | ||
| std::move(init_cb_).Run(status); | ||
| } else { | ||
| client_->OnError(status); | ||
| } |
There was a problem hiding this comment.
All other methods in this class check if client_ is null before invoking callbacks on it. NotifyError should also check if client_ is null to prevent potential null pointer dereference crashes.
| void UrlPlayerRenderer::NotifyError(PipelineStatus status) { | |
| if (init_cb_) { | |
| std::move(init_cb_).Run(status); | |
| } else { | |
| client_->OnError(status); | |
| } | |
| void UrlPlayerRenderer::NotifyError(PipelineStatus status) { | |
| if (init_cb_) { | |
| std::move(init_cb_).Run(status); | |
| } else if (client_) { | |
| client_->OnError(status); | |
| } |
| if (video_frames_decoded > last_video_frames_decoded_) { | ||
| statistics.video_frames_decoded = | ||
| video_frames_decoded - last_video_frames_decoded_; | ||
| last_video_frames_decoded_ = video_frames_decoded; | ||
| } | ||
|
|
||
| if (video_frames_dropped > last_video_frames_dropped_) { | ||
| statistics.video_frames_dropped = | ||
| video_frames_dropped - last_video_frames_dropped_; | ||
| last_video_frames_dropped_ = video_frames_dropped; | ||
| } |
There was a problem hiding this comment.
If the underlying player is recreated or reset (e.g., after a seek or loop), the decoded/dropped frame counters can reset to 0. If video_frames_decoded becomes less than last_video_frames_decoded_, the statistics will not be updated correctly until the new count exceeds the old high value. Handling counter resets ensures accurate statistics reporting.
if (video_frames_decoded >= last_video_frames_decoded_) {
statistics.video_frames_decoded =
video_frames_decoded - last_video_frames_decoded_;
} else {
statistics.video_frames_decoded = video_frames_decoded;
}
last_video_frames_decoded_ = video_frames_decoded;
if (video_frames_dropped >= last_video_frames_dropped_) {
statistics.video_frames_dropped =
video_frames_dropped - last_video_frames_dropped_;
} else {
statistics.video_frames_dropped = video_frames_dropped;
}
last_video_frames_dropped_ = video_frames_dropped;| #define BUILDFLAG_INTERNAL_USE_STARBOARD_MEDIA() (0) | ||
| #endif | ||
|
|
||
| #if defined(ENABLE_BUILDFLAG_USE_STARBOARD_URL_PLAYER) |
There was a problem hiding this comment.
Same as #10979 (comment), I think introducing one more flag for a single Starboard platform is not ideal.
|
Thanks for all review comments. We are moving ahead with approach in PR #10698 (UrlPlayer using StarboardRenderer). |
Implement UrlPlayerRendererClient for the renderer-process side of
URL-based playback on tvOS. The client extends MojoRendererWrapper,
invokes InitializeWithUrl() on the Mojo renderer pipe, and implements
StarboardRendererClientExtension callbacks for video-hole painting,
rendering mode updates, and SbWindow handle delivery.
Add MediaResource::GetMediaUrl() behind USE_STARBOARD_URL_PLAYER so
URL-based MediaResource implementations can provide the source URL.
The default returns an empty GURL; UrlPlayerDemuxer overrides it in
a later CL.
Build: ninja -C out/tvos-arm64-simulator_qa cobalt nplb media_unittests
Bug: 512045535